import { ESJEditingBindModeType, ESViewShed, getDistancesFromPositions } from "earthsdk3";
import { CzmESObjectWithLocation } from "../../../CzmObjects";
import { ESCesiumViewer } from "../../../ESCesiumViewer";
import { flyWithPosition, getCameraPosition, positionToCartesian } from "../../../utils";
import { createNextAnimateFrameEvent } from "xbsj-base";
import { XbsjViewshed } from "./XbsjViewshed";
import * as Cesium from 'cesium';

export class CzmESViewShed extends CzmESObjectWithLocation<ESViewShed> {
    static readonly type = this.register("ESCesiumViewer", ESViewShed.type, this);
    override editingBindMode: ESJEditingBindModeType = 'doublePoints';
    private _czmViewShed;
    get czmViewShed() { return this._czmViewShed; }

    constructor(sceneObject: ESViewShed, czmViewer: ESCesiumViewer) {
        super(sceneObject, czmViewer);
        const viewer = czmViewer.viewer;
        if (!viewer) {
            console.warn(`viewer is undefined!`);
            return;
        }
        this._czmViewShed = this.disposeVar(new XbsjViewshed(viewer.scene));
        const czmViewShed = this._czmViewShed;
        //@ts-ignore
        czmViewShed.ESSceneObjectID = sceneObject.id;
        viewer.scene.primitives.add(czmViewShed);
        this.dispose(() => viewer.scene.primitives.remove(czmViewShed));
        {
            const update = () => {
                if (sceneObject.fov == 0 || sceneObject.aspectRatio == 0) return;
                const fovH = Cesium.Math.toRadians(sceneObject.fov);
                const fovV = Cesium.Math.toRadians(sceneObject.fov / sceneObject.aspectRatio);
                const defaultValue = Cesium.Math.PI - 0.00001;
                czmViewShed.fovH = Cesium.Math.clamp(fovH, -defaultValue, defaultValue);
                czmViewShed.fovV = Cesium.Math.clamp(fovV, -defaultValue, defaultValue);
            }
            update();
            const updateEvent = this.disposeVar(createNextAnimateFrameEvent(
                sceneObject.fovChanged,
                sceneObject.aspectRatioChanged,
            ));
            this.dispose(updateEvent.disposableOn(update));
        }

        {
            const update = () => {
                czmViewShed.enabled = !!sceneObject.position && sceneObject.show;
                if (czmViewShed.position) {
                    czmViewShed.position = positionToCartesian(sceneObject.position);
                }
            };
            update();
            const updateEvent = this.disposeVar(createNextAnimateFrameEvent(
                sceneObject.positionChanged,
                sceneObject.showChanged,
            ));
            this.dispose(updateEvent.disposableOn(update));
        }
        {
            const update = () => {
                czmViewShed.heading = Cesium.Math.toRadians(sceneObject.rotation[0] + 90);
                czmViewShed.pitch = Cesium.Math.toRadians(sceneObject.rotation[1]);
                czmViewShed.roll = Cesium.Math.toRadians(sceneObject.rotation[2]);
            }
            update();
            this.ad(sceneObject.rotationChanged.don(update));
        }
        {
            const update = () => {
                czmViewShed.showHelper = sceneObject.showFrustum ?? ESViewShed.defaults.showFrustum;
            }
            update();
            this.ad(sceneObject.showFrustumChanged.don(update));
        }
        {
            const update = () => {
                if (sceneObject.far < sceneObject.near) {
                    sceneObject.far = sceneObject.near;
                    return
                };
                czmViewShed.far = sceneObject.far;
            }
            update();
            this.ad(sceneObject.farChanged.don(update));
        }
        {
            const update = () => {
                if (sceneObject.near > sceneObject.far || sceneObject.near <= 0) return;
                czmViewShed.near = sceneObject.near;
            }
            update();
            this.ad(sceneObject.nearChanged.don(update));
        }
    }
    override flyTo(duration: number | undefined, id: number): boolean {
        const { sceneObject, czmViewer } = this;
        if (!czmViewer.actived) return false;
        if (sceneObject.flyToParam || sceneObject.flyInParam) {
            return super.flyTo(duration, id);
        } else {
            const viewDistance = (sceneObject.far ?? ESViewShed.defaults.far);
            if (sceneObject.position) {
                flyWithPosition(czmViewer, sceneObject, id, sceneObject.position, viewDistance, duration);
                return true;
            }
            return false;
        }
    }
    override visibleDistance(sceneObject: ESViewShed, czmViewer: ESCesiumViewer): void {
        if (czmViewer.viewer?.camera && sceneObject.show) {
            const dis = getDistancesFromPositions([sceneObject.position, getCameraPosition(czmViewer.viewer.camera)], 'NONE')[0];
            let show = false;
            if (sceneObject.minVisibleDistance < sceneObject.maxVisibleDistance) {
                show = sceneObject.minVisibleDistance < dis && dis < sceneObject.maxVisibleDistance;
            } else if (sceneObject.maxVisibleDistance == 0) {
                show = dis > sceneObject.minVisibleDistance;
            }
            if (this.czmViewShed) {
                this.czmViewShed.showHelper = show && (sceneObject.showFrustum ?? ESViewShed.defaults.showFrustum);
                this.czmViewShed.enabled = show && sceneObject.show && !!sceneObject.position;
            }
        }
    }
}
